#include "../../Coroutine/Scheduler.hpp"
#include "../../Coroutine/Thread.hpp"
#include "../../base/BaseLog.hpp"
#include "../../Coroutine/Channel.hpp"
#include <cassert>
#include <memory>
#include <iostream>
#include <vector>

using namespace DBServer::coroutine;

char signal = 't';

#define TEST(X)                                                            \
    debug("thread(%d) fiber(%d) --> begin\n", Thread::ThreadId(), X);      \
    chan##X < &signal;                                                     \
    Fiber::Yield();                                                        \
    debug("thread(%d) fiber%d --> fisrt resume\n", Thread::ThreadId(), X); \
    chan##X < &signal;                                                     \
    Fiber::Yield();                                                        \
    debug("thread(%d) fiber%d --> second resume\n", Thread::ThreadId(), X)

#define chan(x) chan ## x

#define main_fun_do(X)                                     \
    chan##X > buf;                                         \
    while (fibers[X - 1]->get_state() != FiberState::HOLD) \
        ;                                                  \
    fibers[X - 1]->set_state(FiberState::READY);           \
    scher->schedule(fibers[X - 1])

#define fiber(X)                                              \
    Fiber::ptr fiber_##X = std::make_shared<Fiber>(fiber##X); \
    fibers.push_back(fiber_##X);                              \
    scher->schedule(fibers[X-1])

std::vector<Channel> chans = []() {
    std::vector<Channel> chans;
    chans.push_back(Channel{});
    chans.push_back(Channel{});
    chans.push_back(Channel{});
    chans.push_back(Channel{});
    chans.push_back(Channel{});
    return chans;
}();

Channel chan1{}, chan2{}, chan3{}, chan4{}, chan5{};

void fiber1() { TEST(1); }
void fiber2() { TEST(2); }
void fiber3() { TEST(3); }
void fiber4() { TEST(4); }
void fiber5() { TEST(5); }

void test_singel_thread() {
    info("---------------test singel thread---------------\n");
    Scheduler::ptr scher = std::make_shared<Scheduler>(1, false, "singel thread scheduler");

    Fiber::ptr fiber_1 = std::make_shared<Fiber>(fiber1);
    Fiber::ptr fiber_2 = std::make_shared<Fiber>(fiber2);
    Fiber::ptr fiber_3 = std::make_shared<Fiber>(fiber3);

    std::vector<Fiber::ptr> fibers;
    fibers.push_back(fiber_1);
    fibers.push_back(fiber_2);
    fibers.push_back(fiber_3);

    scher->start();

    scher->schedule(fibers[0]);
    scher->schedule(fibers[1]);
    scher->schedule(fibers[2]);

    char buf[1];

    chan1 > buf;
    while (fibers[0]->get_state() != FiberState::HOLD)
        ;
    fibers[0]->set_state(FiberState::READY);
    scher->schedule(fibers[0]);

    chan2 > buf;
    while (fibers[1]->get_state() != FiberState::HOLD)
        ;
    fibers[1]->set_state(FiberState::READY);
    scher->schedule(fibers[1]);

    chan3 > buf;
    while (fibers[2]->get_state() != FiberState::HOLD)
        ;
    fibers[2]->set_state(FiberState::READY);
    scher->schedule(fibers[2]);

    chan1 > buf;
    while (fibers[0]->get_state() != FiberState::HOLD)
        ;
    fibers[0]->set_state(FiberState::READY);
    scher->schedule(fibers[0]);

    chan2 > buf;
    while (fibers[1]->get_state() != FiberState::HOLD)
        ;
    fibers[1]->set_state(FiberState::READY);
    scher->schedule(fibers[1]);

    chan3 > buf;
    while (fibers[2]->get_state() != FiberState::HOLD)
        ;
    fibers[2]->set_state(FiberState::READY);
    scher->schedule(fibers[2]);

    scher->stop();

    fiber_1.reset();
    fiber_2.reset();
    fiber_3.reset();

    info("--------------singel thread test pass---------------\n");

}

void test_multi_thread() {
    info("Thread id (%d)\n", Thread::ThreadId());
    int i_;
    std::cin >> i_;
    info("---------------test multi thread---------------\n");
    Scheduler::ptr scher = std::make_shared<Scheduler>(1, false, "mutil thread scheduler");

    std::vector<Fiber::ptr> fibers;
 //   Fiber::ptr fiber_1 = std::make_shared<Fiber>(fiber1);
 //   fibers.push_back(fiber_1);
 //   scher->schedule(fibers[0]);
    fiber(1);
    fiber(2);
    fiber(3);
    fiber(4);
    fiber(5);

    int i;

   // std::cin >> i;
    scher->start();

  //  std::cin >> i;

    char buf[1];
    main_fun_do(1);
    main_fun_do(2);
    main_fun_do(3);
    main_fun_do(4);
    main_fun_do(5);

 //   std::cin >> i;

    main_fun_do(1);
    main_fun_do(2);
    main_fun_do(3);
    main_fun_do(4);
    main_fun_do(5);

   // std::cin >> i;

    scher->stop();

    fiber_1.reset();
    fiber_2.reset();
    fiber_3.reset();
    fiber_4.reset();
    fiber_5.reset();

    info("--------------mutil thread test pass---------------\n");
}

int main(int argc, const char** argv) {

    test_singel_thread();
    
    int i;
    std::cin >> i;

    test_multi_thread();
    return 0;
}